package com.yincheng.oes.business.exam.export.analysispaper.service;

import com.yincheng.oes.attachment.entity.Attachment;
import com.yincheng.oes.attachment.mapper.AttachmentMapper;
import com.yincheng.oes.business.base.entity.Question;
import com.yincheng.oes.business.consist.ResultCodeEnum;
import com.yincheng.oes.business.exam.base.entity.Paper;
import com.yincheng.oes.business.exam.base.mapper.PaperMapper;
import com.yincheng.oes.business.exam.export.analysispaper.entity.*;
import com.yincheng.oes.business.exam.export.analysispaper.mapper.PaperResultMapper;
import com.yincheng.oes.business.exam.export.model.ExportAnalysisParam;
import com.yincheng.oes.business.model.result.BusinessResult;
import com.yincheng.oes.business.util.*;
import com.yincheng.oes.business.util.echartconvert.EchartsUtil;
import com.yincheng.oes.sso.util.UserUtils;
import freemarker.template.TemplateException;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import static com.yincheng.oes.business.util.DigitalUtils.calIncreasePercentage;
import static com.yincheng.oes.business.util.DigitalUtils.calProportionPercentage;

@Service
public class AnalysisPaperService {
    private static final Logger logger = LogManager.getLogger(AnalysisPaperService.class);

    private static final String IN_SUFFIX = "入科";

    /**
     * doc文件名后缀
     */
    private static final String DOC_SUFFIX = ".doc";

    @Autowired
    private PaperResultMapper paperResultMapper;
    @Autowired
    private PaperMapper paperMapper;

    @Autowired
    private AttachmentMapper attachmentMapper;

    /**
     * 线程池，用来执行导出程序
     */
    private final static ExecutorService singleExecutor = Executors.newSingleThreadExecutor();

    public BusinessResult exportZipStudentAnalysisPapers(ExportAnalysisParam param) {
        logger.debug("start export student analysis paper, param is {}", param);
        BusinessResult businessResult = new BusinessResult(ResultCodeEnum.SUCCESS);
        String userId = UserUtils.getUser().getUserId();
        singleExecutor.execute(()->{
            try {
                long start = System.currentTimeMillis();
                exportZipStudentAnalysisPapersSync(param, userId);
                logger.debug("export student analysis paper cost {} ms", System.currentTimeMillis()-start);
            } catch (IOException | TemplateException e) {
                logger.error("export student analysis paper error", e);
            }
        });
        return businessResult;
    }

    private void exportZipStudentAnalysisPapersSync(ExportAnalysisParam param, String userId)
            throws IOException, TemplateException {
        BusinessResult businessResult = new BusinessResult(ResultCodeEnum.SUCCESS);
        Paper paper = paperMapper.selectById(param.getPaperId1());
        String paperName = paper.getPaperName();
        List<AnalysisResult> analysisPaperResults = getAnalysisPaperResult(param);
        String fileName = paperName + "_测评分析报告_" + TimeUtils.getYyMMddHHmm() + ".zip";
        File zipFile = getZipFile(analysisPaperResults, fileName);
        businessResult.setMessage("export student analysis paper success");
        //生成attachment并保存到数据库
        Attachment attachment =  AttachmentUtils.getAttachment(fileName,zipFile.length(),".zip",userId);
        attachmentMapper.insert(attachment);
    }

    private File getZipFile(List<AnalysisResult> analysisPaperResults, String fileName) throws IOException, TemplateException {
        File zipFile = FileUtils.getFile(ExportFileUtils.getTmpBaseFile(), fileName);
        try (ZipArchiveOutputStream zaos = new ZipArchiveOutputStream(zipFile)) {
            for (AnalysisResult analysisPaperResult : analysisPaperResults) {
                String tempDocFileName = UUID.randomUUID()+ DOC_SUFFIX;
                File myWord =  FileUtils.getFile(ExportFileUtils.getTmpBaseFile(), tempDocFileName);
                FileOutputStream fos = new FileOutputStream(myWord);
                buildExportWord(fos, analysisPaperResult);
                String docFileName = getDocName(analysisPaperResult);
                ZipArchiveEntry archiveEntry = new ZipArchiveEntry(docFileName);
                archiveEntry.setSize(myWord.length());
                zaos.putArchiveEntry(archiveEntry);
                try {
                    FileUtils.copyFile(myWord, zaos);
                } finally {
                    zaos.closeArchiveEntry();
                    myWord.deleteOnExit();
                }
            }
        }
        return zipFile;
    }

    private String getDocName(AnalysisResult analysisPaperResult) {
        StringBuilder sb = new StringBuilder();
        if(StringUtils.isEmpty(analysisPaperResult.getAssistantName())){
            sb.append("助教空");
        }else {
            sb.append(analysisPaperResult.getAssistantName());
        }
        sb.append("--");
        sb.append(analysisPaperResult.getStudentName());
        sb.append(DOC_SUFFIX);
        return sb.toString();
    }


    private void buildExportWord(FileOutputStream os, AnalysisResult analysisPaperResult)
            throws IOException, TemplateException {
        Map<String, Object> dataMap = new HashMap<>();
        dataMap.put("analysisResult", analysisPaperResult);
        List<String> answers = PaperUtils.getAnswersList();
        dataMap.put("answers", answers);

        String fileName = "2021银成医考执业-模板.ftl";
        ExportWord.exportWord(dataMap, fileName, os);
    }

    public List<AnalysisResult> getAnalysisPaperResult(ExportAnalysisParam param) {
        List<AnalysisResult> analysisResults = new ArrayList<>();

        // 获取出入测评的A2类型题目集合
        Map<String, List<Question>> a2QuestionMap = new HashMap<>();
        ExportAnalysisParam param1 = (ExportAnalysisParam) param.clone();
        param1.setPaperId(param1.getPaperId1());
        ExportAnalysisParam param2 = (ExportAnalysisParam) param.clone();
        param2.setPaperId(param2.getPaperId2());
        List<Question> a2Questions1 = paperResultMapper.selectA2QuestionsByParam(param1);
        List<Question> a2Questions2 = paperResultMapper.selectA2QuestionsByParam(param2);
        a2Questions1.addAll(a2Questions2);
        List<Question> a2Questions = a2Questions1.stream().filter(distinctByKey(Question::getId))
                .collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(a2Questions)) {
            a2Questions.forEach(question -> a2QuestionMap.computeIfAbsent(question.getRelationCode(), k -> new ArrayList<>()).add(question));
        }

        // 1.获取已完成的试卷集合
        List<PaperResult> paperResults1 = paperResultMapper.selectPaperResultsByParam(param1);
        List<PaperResult> paperResults2 = paperResultMapper.selectPaperResultsByParam(param2);
        if (CollectionUtils.isEmpty(paperResults1) || CollectionUtils.isEmpty(paperResults2)) {
            return analysisResults;
        }

        // 2.根据时间判断出入试卷
        List<PaperResult> paperResultsIn;
        List<PaperResult> paperResultsOut;

        PaperResult paperResult1 = paperResults1.get(0);
        PaperResult paperResult2 = paperResults2.get(0);
        if (!paperResult1.getPaperName().contains(IN_SUFFIX) && !paperResult2.getPaperName().contains(IN_SUFFIX)) {
            return analysisResults;
        }
        if (paperResult1.getPaperName().contains(IN_SUFFIX)) {
            paperResultsIn = paperResults1;
            paperResultsOut = paperResults2;

        } else {
            paperResultsIn = paperResults2;
            paperResultsOut = paperResults1;
        }
        analysisResults = getResutls(a2QuestionMap, paperResultsIn, paperResultsOut, param);

        return analysisResults;
    }

    private <T> Predicate<T> distinctByKey(Function<? super T, Object> keyExtractor) {
        Map<Object, Boolean> seen = new ConcurrentHashMap<>();
        return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
    }

    private List<AnalysisResult> getResutls(Map<String, List<Question>> a2QuestionMap, List<PaperResult> paperResultsIn,
            List<PaperResult> paperResultsOut, ExportAnalysisParam param) {
        List<AnalysisResult> analysisResults = new ArrayList<>();
        // 3.比较获得两个试卷都完成的试卷集合
        List<PaperResult> paperResultsFiltered1 = new ArrayList<>();
        List<PaperResult> paperResultsFiltered2 = new ArrayList<>();

        List<String> studentIds2 = paperResultsOut.stream().map(PaperResult::getStudentId).collect(Collectors.toList());
        Map<String, PaperResult> paperResults2Map = paperResultsOut.stream()
                .collect(Collectors.toMap(PaperResult::getStudentId, Function.identity()));
        paperResultsIn.forEach(e -> {
            if (isInPaperResults(e.getStudentId(), studentIds2)) {
                paperResultsFiltered1.add(e);
                paperResultsFiltered2.add(paperResults2Map.get(e.getStudentId()));
            }
        });
        if (paperResultsFiltered1.size() == 0) {
            return analysisResults;
        }

        // 4.批量获取每个同学试卷1和2的错误知识点----知识点和题目id需要关联
        for (int i = 0; i < paperResultsFiltered1.size(); i++) {
            AnalysisResult analysisResult = new AnalysisResult();

            PaperResult paperResp1 = paperResultsFiltered1.get(i);
            PaperResult paperResp2 = paperResultsFiltered2.get(i);
            if (Objects.isNull(paperResp1) || Objects.isNull(paperResp2)) {
                continue;
            }

            // 设置试卷基本信息
            analysisResult.setSubjectName(param.getSubjectName());
            analysisResult.setCountryAvg(param.getCountryAvg());
            analysisResult.setReviseScore(param.getReviseScore());
            analysisResult.setStudentName(paperResp1.getStudentName());
            analysisResult.setAssistantName(paperResp1.getAssistantName());

            Double totalScore = paperResp1.getTotalScore();
            String score1 = calProportionPercentage(paperResp1.getScore(),totalScore);
            String score2 = calProportionPercentage(paperResp2.getScore(),totalScore);
            analysisResult.setScore1(score1);
            analysisResult.setScore2(score2);
            String increaseRate = calIncreasePercentage(score1,score2);
            analysisResult.setIncreaseRate(increaseRate);
            String compareRate = calIncreasePercentage(param.getReviseScore(),score2);
            analysisResult.setCompareRate(compareRate);
            String paperName = "测评分析报告";
            String[] names = paperResp1.getPaperName().split(IN_SUFFIX);
            if (names.length > 0 && StringUtils.isNotEmpty(names[0])) {
                paperName = names[0] + paperName;
            }
            analysisResult.setPaperName(paperName);

            List<AnalysisKnowledge> knowleges1 = new ArrayList<>();
            List<AnalysisKnowledge> knowleges2 = new ArrayList<>();
            List<AnalysisKnowledge> knowleges3 = new ArrayList<>();
            List<Question> question1 = new ArrayList<>();
            List<Question> question2 = new ArrayList<>();
            List<Question> question3 = new ArrayList<>();// 暂时将入科和出科中的错题都添加进去 // 再去重
            List<AnalysisKnowledge> wrongKnowleges1 = paperResp1.getWrongKnowleges();
            List<AnalysisKnowledge> wrongKnowleges2 = paperResp2.getWrongKnowleges();
            if (CollectionUtils.isEmpty(wrongKnowleges1)) {
                knowleges2 = wrongKnowleges2 == null ? new ArrayList<>() : wrongKnowleges2;
            } else if (CollectionUtils.isEmpty(wrongKnowleges2)) {
                knowleges1 = wrongKnowleges1;
            } else {
                Map<Integer, Question> questionMap1 = paperResp1.getWrongQuestions().stream()
                        .collect(Collectors.toMap(Question::getId, Function.identity()));
                Map<Integer, Question> questionMap2 = paperResp2.getWrongQuestions().stream()
                        .collect(Collectors.toMap(Question::getId, Function.identity()));
                Map<Integer, AnalysisKnowledge> wrongKnowlegesMap2 = wrongKnowleges2.stream().distinct()
                        .collect(Collectors.toMap(AnalysisKnowledge::getId, val -> val, (key1, key2) -> key1));
                for (AnalysisKnowledge analysisKnowledge : wrongKnowleges1) {
                    Integer id = analysisKnowledge.getId();
                    if (Objects.isNull(wrongKnowlegesMap2.get(id))) {
                        knowleges1.add(analysisKnowledge);
                        question1.add(questionMap1.get(analysisKnowledge.getQuesId()));
                    } else {
                        knowleges3.add(analysisKnowledge);
                        question3.add(questionMap1.get(analysisKnowledge.getQuesId()));
                        question3.add(questionMap2.get(wrongKnowlegesMap2.get(id).getQuesId()));
                        wrongKnowlegesMap2.remove(id);
                    }
                }
                for (Entry<Integer, AnalysisKnowledge> wrongKnowlegeMap : wrongKnowlegesMap2.entrySet()) {
                    knowleges2.add(wrongKnowlegeMap.getValue());
                    question2.add(questionMap2.get(wrongKnowlegeMap.getValue().getQuesId()));
                }
                question3 = question3.stream().distinct().collect(Collectors.toList());
            }
            setIndex(knowleges1);
            List<AnalysisKnowledge> newKnowleges1 = knowleges1.stream().filter(Objects::nonNull)
                    .collect(Collectors.toList());
            analysisResult.setKnowleges1(newKnowleges1);
            setIndex(knowleges2);
            List<AnalysisKnowledge> newKnowleges2 = knowleges2.stream().filter(Objects::nonNull)
                    .collect(Collectors.toList());
            analysisResult.setKnowleges2(newKnowleges2);
            setIndex(knowleges3);
            List<AnalysisKnowledge> newKnowleges3 = knowleges3.stream().filter(Objects::nonNull)
                    .collect(Collectors.toList());
            analysisResult.setKnowleges3(newKnowleges3);

            List<AnalysisQuestionWrapper> analysisQuestionWrappers1 = getQuestions(question1, a2QuestionMap);
            List<AnalysisQuestionWrapper> newAnalysisQuestionWrappers1 = analysisQuestionWrappers1.stream()
                    .filter(Objects::nonNull).collect(Collectors.toList());
            analysisResult.setQuestion1(newAnalysisQuestionWrappers1);
            List<List<ReferAnswer>> answers1 = getAnswers(analysisQuestionWrappers1);
            List<List<ReferAnswer>> newAnswers1 = answers1.stream().filter(CollectionUtils::isNotEmpty)
                    .collect(Collectors.toList());
            analysisResult.setAnswers1(newAnswers1);

            List<AnalysisQuestionWrapper> analysisQuestionWrappers2 = getQuestions(question2, a2QuestionMap);
            List<AnalysisQuestionWrapper> newAnalysisQuestionWrappers2 = analysisQuestionWrappers2.stream()
                    .filter(Objects::nonNull).collect(Collectors.toList());
            analysisResult.setQuestion2(newAnalysisQuestionWrappers2);
            List<List<ReferAnswer>> answers2 = getAnswers(analysisQuestionWrappers2);
            List<List<ReferAnswer>> newAnswers2 = answers2.stream().filter(CollectionUtils::isNotEmpty)
                    .collect(Collectors.toList());
            analysisResult.setAnswers2(newAnswers2);

            List<AnalysisQuestionWrapper> analysisQuestionWrappers3 = getQuestions(question3, a2QuestionMap);
            List<AnalysisQuestionWrapper> newAnalysisQuestionWrappers3 = analysisQuestionWrappers3.stream()
                    .filter(Objects::nonNull).collect(Collectors.toList());
            analysisResult.setQuestion3(newAnalysisQuestionWrappers3);
            List<List<ReferAnswer>> answers3 = getAnswers(analysisQuestionWrappers3);
            List<List<ReferAnswer>> newAnswers3 = answers3.stream().filter(CollectionUtils::isNotEmpty)
                    .collect(Collectors.toList());
            analysisResult.setAnswers3(newAnswers3);

            // 设置测评柱状图图片
            List<Double> scores = new ArrayList<>();
            scores.add(Double.valueOf(analysisResult.getScore1()));
            scores.add(Double.valueOf(analysisResult.getScore2()));
            List<Double> countryScores = new ArrayList<>();
            countryScores.add(Double.valueOf(analysisResult.getCountryAvg()));
            countryScores.add(Double.valueOf(analysisResult.getCountryAvg()));
            List<Double> reviseScores = new ArrayList<>();
            reviseScores.add(Double.valueOf(analysisResult.getReviseScore()));
            reviseScores.add(Double.valueOf(analysisResult.getReviseScore()));
            try {
                String base64 = EchartsUtil.generateEchartsBase64(scores, countryScores, reviseScores);
                analysisResult.setImgUrl(base64);
            } catch (TemplateException | IOException e) {
                logger.error("generate echarts img error",e);
            }
            analysisResults.add(analysisResult);
        }

        return analysisResults;
    }

    private void setIndex(List<AnalysisKnowledge> knowledgeList) {
        for (int i = 0; i < knowledgeList.size(); i++) {
            knowledgeList.get(i).setIndex(i + 1);
        }
    }

    private List<List<ReferAnswer>> getAnswers(List<AnalysisQuestionWrapper> analysisQuestionWrappers) {
        List<List<ReferAnswer>> answers = new ArrayList<>();// 错题集答案

        List<ReferAnswer> list = new ArrayList<>();
        analysisQuestionWrappers.forEach(questionWrapper -> questionWrapper.getQuestionDetails().forEach(questionDetail -> {
            ReferAnswer referAnswer = new ReferAnswer();
            referAnswer.setAnswer(questionDetail.getAnswer());
            referAnswer.setIndex(questionDetail.getIndex());
            list.add(referAnswer);
        }));
        int group = 5;
        for (int i = 0; i < (list.size() - 1) / group + 1; i++) {
            List<ReferAnswer> referAnswers = new ArrayList<>();
            for (int n = 0; n < group; n++) {
                if ((i * 5 + n) < list.size()) {
                    ReferAnswer referAnswer = new ReferAnswer();
                    referAnswer.setAnswer(list.get(i * 5 + n).getAnswer());
                    referAnswer.setIndex(list.get(i * 5 + n).getIndex());
                    referAnswers.add(referAnswer);
                } else {
                    break;
                }

            }
            answers.add(referAnswers);
        }

        return answers;
    }

    private AnalysisQuestionDetailWrapper getAnalysisQuestionDetailWrapper(int index, Question question) {
        AnalysisQuestionDetailWrapper analysisQuestionDetailWrapper = new AnalysisQuestionDetailWrapper();
        analysisQuestionDetailWrapper.setIndex(getIndex(index));
        analysisQuestionDetailWrapper.setStem(question.getStem());
        analysisQuestionDetailWrapper.setOptionA(question.getOptionA());
        analysisQuestionDetailWrapper.setOptionB(question.getOptionB());
        analysisQuestionDetailWrapper.setOptionC(question.getOptionC());
        analysisQuestionDetailWrapper.setOptionD(question.getOptionD());
        analysisQuestionDetailWrapper.setOptionE(question.getOptionE());
        analysisQuestionDetailWrapper.setOptionF(question.getOptionF());
        analysisQuestionDetailWrapper.setAnswer(question.getAnswer());
        return analysisQuestionDetailWrapper;
    }

    private boolean isInPaperResults(String studentId, List<String> studentIds) {
        return studentIds.contains(studentId);
    }

    private String getIndex(int index) {
        return String.format("%03d", index);
    }

    private List<AnalysisQuestionWrapper> getQuestions(List<Question> questions,
            Map<String, List<Question>> questionMap) {
        List<AnalysisQuestionWrapper> analysisQuestionWrappers = new ArrayList<>();// 错题集

        List<String> relationCodes = new ArrayList<>();// 错题集中A2类型relationCode集合
        if (CollectionUtils.isNotEmpty(questions)) {
            int index = 0;
            for (Question question : questions) {
                if ("2".equals(question.getQuesType())) {
                    if (!relationCodes.contains(question.getRelationCode())) {
                        AnalysisQuestionWrapper analysisQuestionWrapper = new AnalysisQuestionWrapper();
                        // analysisQuestionWrapper.setQuesCode(question.getQuesCode());
                        analysisQuestionWrapper.setQuestion(question.getQuestion());
                        analysisQuestionWrapper.setQuesType(question.getQuesType());
                        List<AnalysisQuestionDetailWrapper> questionDetails = new ArrayList<>();

                        // 将题目中的A2型题目补充完毕，并按照题型重新按照顺序包装
                        List<Question> a2Questions = questionMap.get(question.getRelationCode());
                        int shareCount = 0;
                        if (CollectionUtils.isNotEmpty(a2Questions)) {
                            for (Question a2Question : a2Questions) {

                                // 设置每个题目信息
                                index++;
                                AnalysisQuestionDetailWrapper analysisQuestionDetailWrapper = getAnalysisQuestionDetailWrapper(
                                        index, a2Question);
                                questionDetails.add(analysisQuestionDetailWrapper);

                                shareCount++;
                            }
                        }

                        analysisQuestionWrapper.setQuestionDetails(questionDetails);
                        analysisQuestionWrapper.setShareCount(shareCount);
                        relationCodes.add(question.getRelationCode());
                        analysisQuestionWrappers.add(analysisQuestionWrapper);
                    }
                } else {

                    AnalysisQuestionWrapper analysisQuestionWrapper = new AnalysisQuestionWrapper();
                    analysisQuestionWrapper.setQuesCode(question.getQuesCode());
                    analysisQuestionWrapper.setQuestion(question.getQuestion());
                    analysisQuestionWrapper.setQuesType(question.getQuesType());
                    List<AnalysisQuestionDetailWrapper> questionDetails = new ArrayList<>();

                    // 设置每个题目信息
                    index++;
                    AnalysisQuestionDetailWrapper analysisQuestionDetailWrapper = getAnalysisQuestionDetailWrapper(
                            index, question);
                    questionDetails.add(analysisQuestionDetailWrapper);
                    analysisQuestionWrapper.setQuestionDetails(questionDetails);
                    analysisQuestionWrappers.add(analysisQuestionWrapper);
                }
            }
        }
        return analysisQuestionWrappers;
    }
}
